<?php
class payments {
  function __construct() {
  }

  public function listcat() {
	$errcode = 0;
	$errmess = '';
    $data = array();
	
    try
    {
// Только зарегистрированные пользователи могут видеть данные
		if( !isset($_SESSION['user_id']) || $_SESSION['user_id'] == 0 )
			throw new Exception(ERR_M::TIMEOUT_ERROR, 999);

// Если есть разрешение видеть хоть, что-нибуть, то маска должна быть READ_ANY
		$rwd = $_SESSION['priv']->jobcats->rwd;
		if( ( $rwd & PERM_BITS::READ_ANY ) == 0 ) throw new Exception(ERR_M::DATA_OPER_DISABLED, 99);

// Открываем базу данных
		$db = App::me()->dbOpen();	

// Фильтр привилегий
		if( $rwd & PERM_BITS::READ_OTH ) $whs = '1 = 1';
		elseif( $rwd & PERM_BITS::READ_GRP ) 
			$whs = 'jct_user_id IN (SELECT user_id FROM users WHERE cat_id = '.$_SESSION['cat_id'].')';
		elseif( $rwd & PERM_BITS::READ_OWN ) $whs = 'jct_user_id = '.$_SESSION['user_id'];
		else $whs = '1 = 0';
		
// Формируем запрос
		$sql =	'SELECT * FROM jobcats WHERE '.$whs.' ORDER BY jct_order';
		$rs = $db->query($sql);                  
        while( $row = $rs->fetchObject() ) {
            $unit = new stdClass;
            $unit->value = $row->jct_id;
			$unit->name = $row->jct_name;
            $data[] = $unit;
        }
        $rs->closeCursor();
 
    }
	catch (PDOException $e) {
		$errcode = 1;
		$errmess = ERR_M::DATABASE_ERROR;
	}
    catch ( Exception $e ) {
		$errcode = $e->getCode();
		$errmess = $e->getMessage();
	}

	return $data;
  }
  
  public function listjtp() {
	$errcode = 0;
	$errmess = '';
    $data = array();
	
    try
    {
// Только зарегистрированные пользователи могут видеть данные
		if( !isset($_SESSION['user_id']) || $_SESSION['user_id'] == 0 )
			throw new Exception(ERR_M::TIMEOUT_ERROR, 999);

// Если есть разрешение видеть хоть, что-нибуть, то маска должна быть READ_ANY
		$rwd = $_SESSION['priv']->jobtypes->rwd;
		if( ( $rwd & PERM_BITS::READ_ANY ) == 0 ) throw new Exception(ERR_M::DATA_OPER_DISABLED, 99);

// Открываем базу данных
		$db = App::me()->dbOpen();	

// Фильтр привилегий
		if( $rwd & PERM_BITS::READ_OTH ) $whs = '1 = 1';
		elseif( $rwd & PERM_BITS::READ_GRP ) 
			$whs = 'jtp_user_id IN (SELECT user_id FROM users WHERE cat_id = '.$_SESSION['cat_id'].')';
		elseif( $rwd & PERM_BITS::READ_OWN ) $whs = 'jtp_user_id = '.$_SESSION['user_id'];
		else $whs = '1 = 0';
		
// Формируем запрос
		$sql =	'SELECT * FROM jobtypes WHERE '.$whs.' ORDER BY jtp_code';
		$rs = $db->query($sql);                  
        while( $row = $rs->fetch(PDO::FETCH_ASSOC) ) {
            $unit = new stdClass;
            $unit->key = $row['jtp_id'];
			$unit->title = $row['jtp_code'];
			$unit->cat_id = $row['jtp_cat_id'];
			$unit->tooltip = $row['jtp_descr'];
            $unit->isFolder = false;
            $unit->isLazy = false;
            $data[] = $unit;
        }
        $rs->closeCursor();
 
    }
	catch (PDOException $e) {
		$errcode = 1;
		$errmess = ERR_M::DATABASE_ERROR;
	}
    catch ( Exception $e ) {
		$errcode = $e->getCode();
		$errmess = $e->getMessage();
	}

	if( $errcode ) {
		$unit = new stdClass;
		$unit->title = '';
		$unit->errcode = $errcode;
		$unit->errmess = $errmess;
		return array($unit);
	}
	else return $data;
  }
  
  public function findjtp() {
	$errcode = 0;
	$errmess = '';
    $data = array();
	
    try
    {
// Только зарегистрированные пользователи могут видеть данные
		if( !isset($_SESSION['user_id']) || $_SESSION['user_id'] == 0 )
			throw new Exception(ERR_M::TIMEOUT_ERROR, 999);

// Find - услуга общего пользования. Поэтому, маска должна быть READ_ANY
		$rwd = $_SESSION['priv']->jobtypes->rwd;
		if( ( $rwd & PERM_BITS::READ_ANY ) == 0 ) throw new Exception(ERR_M::DATA_OPER_DISABLED, 99);

// Проверяем данные
        $term   = isset($_REQUEST["term"]) ? trim(strip_tags($_REQUEST["term"])) : '';
        if ($term == '')
			throw new Exception(ERR_M::PROTOCOL_ERROR, 1);
   
// Открываем базу данных
		$db = App::me()->dbOpen();	

// Фильтр привилегий
		if( $rwd & PERM_BITS::READ_OTH ) $whs = '1 = 1';
		elseif( $rwd & PERM_BITS::READ_GRP ) 
			$whs = 'jtp_user_id IN (SELECT user_id FROM users WHERE cat_id = '.$_SESSION['cat_id'].')';
		elseif( $rwd & PERM_BITS::READ_OWN ) $whs = 'jtp_user_id = '.$_SESSION['user_id'];
		else $whs = '1 = 0';
		
// Формируем запрос. Выбираем только те виды работ, для которых есть баланс
		$sql =  "SELECT jtp_id, jtp_code, jtp_descr, ".
				"IF(( SELECT 1 FROM baltypes WHERE btp_jtp_id=jtp_id limit 1 ),1,0) AS btp_num ".
				"FROM jobtypes ".
				"WHERE ( jtp_code LIKE ".$db->quote("%".$term."%").
				" OR jtp_descr LIKE ".$db->quote("%".$term."%").' ) AND '.$whs." ORDER BY jtp_code";
					
		$rs = $db->query($sql);                  
        while( $row = $rs->fetch(PDO::FETCH_ASSOC) ) {
			if( !$row['btp_num'] ) continue;
            $item = new stdClass;
            $item->jtp_id = $row['jtp_id'];
			
			$num = mb_strlen($term,'UTF-8');
			$pos = mb_stripos( $row['jtp_code'], $term, 0, 'UTF-8');
			if( !is_bool($pos) ) {
				$bterm = mb_substr($row['jtp_code'], $pos, $num, 'UTF-8');
				$name = preg_replace( '/'.$bterm.'/u', '<b>'.$bterm.'</b>', $row['jtp_code'] );				
			} else $name = $row['jtp_code'];
			$pos = mb_stripos( $row['jtp_descr'], $term, 0, 'UTF-8');
			if( !is_bool($pos) ) {
				$bterm = mb_substr($row['jtp_descr'], $pos, $num, 'UTF-8');
				$full = preg_replace( '/'.$bterm.'/u', '<b>'.$bterm.'</b>', $row['jtp_descr'] );				
			} else $full = $row['jtp_descr'];
			
            $item->jtp_code = $name;
			$item->jtp_descr = $full;
            $data[] = $item;
        }
        $rs->closeCursor();
 
    }
	catch (PDOException $e) {
		$errcode = 1;
		$errmess = ERR_M::DATABASE_ERROR;
	}
    catch ( Exception $e ) {
		$errcode = $e->getCode();
		$errmess = $e->getMessage();
	}
 
	return array('errcode'=>$errcode,'errmess'=>$errmess, 'data'=>$data);
  }
  
  
  public function insjtp() {

    $jtp_code		= isset($_POST['jtp_code']) ? trim(strip_tags($_POST['jtp_code'])) : '';
    $jtp_cat_id	= isset($_POST['jtp_cat_id']) ? trim(strip_tags($_POST['jtp_cat_id'])) : '';
    $jtp_descr	= isset($_POST['jtp_descr']) ? trim(strip_tags($_POST['jtp_descr'])) : '';
	
	$errcode = 0;
	$errmess = '';
	$data = new stdClass;
	
    try
    {
// Только зарегистрированные пользователи могут вносить данные
		if( !isset($_SESSION['user_id']) || $_SESSION['user_id'] == 0 )
			throw new Exception(ERR_M::TIMEOUT_ERROR, 999);
		$jtp_user_id = $_SESSION['user_id'];
		
//Есть ли разрешение на вставку записей
		$rwd = $_SESSION['priv']->jobtypes->rwd;
		if( ($rwd & PERM_BITS::WRITE_OWN) == 0 ) throw new Exception(ERR_M::DATA_OPER_DISABLED, 99);

// Все ли данные переданы
		$errdata = "";
		
// jtp_code
		if( $jtp_code == '' ) $errdata .= ($errdata?'<br>':'')."- Не указан шифр вида работ.";
		else {
			$jtp_code = mb_strtoupper( $jtp_code, 'UTF-8' );
			$jtp_code = preg_replace( '/[^А-ЯІЇA-Z0-9]/u', '', $jtp_code );
			$jtp_code = mb_substr( $jtp_code, 0, 16, 'UTF-8' );
		}

// jtp_cat_id
		if( !$jtp_cat_id || !ctype_digit($jtp_cat_id) )
			$errdata .= ($errdata?'<br>':'')."- Категория работ не указана или указана неверно.";
		
// jtp_descr
		if( $jtp_descr == '' ) $errdata .= ($errdata?'<br>':'')."- Не задано описание вида работ.";
		else $jtp_descr = mb_substr( $jtp_descr, 0, 128, 'UTF-8');

//				
// Если есть ошибки - можно уже закончить
//
		if( $errdata ) throw new Exception($errdata, 1);
		
// Открываем базу данных
		$db = App::me()->dbOpen();
		
// Может быть шифр уже существует
		
		$sqlreq =	'SELECT jtp_code FROM jobtypes WHERE jtp_code = '.$db->quote($jtp_code);
		$rs = $db->query( $sqlreq );		
		$row = $rs->fetch(PDO::FETCH_ASSOC);
		if( $row ) {
			throw new Exception('В справочнике уже есть запись с таким шифром.', 1);
        }    
		$rs->closeCursor();

// Пишем данные
		try { 
			$db->beginTransaction();
			$sql =	'INSERT INTO jobtypes ( jtp_code, jtp_descr, jtp_cat_id, jtp_user_id ) '.
					'VALUES ('. $db->quote($jtp_code).", ".$db->quote($jtp_descr).", ".
							$db->quote($jtp_cat_id).", ".$db->quote($jtp_user_id). ")";
			$db->exec($sql);
			$jtp_id = $db->lastInsertId();
			
			$db->commit();
		} 
		catch(PDOExecption $e) { 
			$db->rollback(); 
			$errcode = 1;
			$errmess = ERR_M::DATABASE_ERROR;
		} 
												
		$data->key = $jtp_id;
		$data->cat_id = $jtp_cat_id;
		$data->title = $jtp_code;
		$data->tooltip = $jtp_descr;
    }
	catch (PDOException $e) {
		$errcode = 1;
		$errmess = ERR_M::DATABASE_ERROR;
	}
    catch ( Exception $e ) {
		$errcode = $e->getCode();
		$errmess = $e->getMessage();
	}

	return array('errcode'=>$errcode,'errmess'=>$errmess, 'data'=>$data);
  }

  
  public function updjtp() {

    $jtp_id		= isset($_POST['jtp_id']) ? trim(strip_tags($_POST['jtp_id'])) : '';
    $jtp_cat_id	= isset($_POST['jtp_cat_id']) ? trim(strip_tags($_POST['jtp_cat_id'])) : '';
    $jtp_code	= isset($_POST['jtp_code']) ? trim(strip_tags($_POST['jtp_code'])) : '';
    $jtp_descr	= isset($_POST['jtp_descr']) ? trim(strip_tags($_POST['jtp_descr'])) : '';
	
	$errcode = 0;
	$errmess = '';
	$data = new stdClass;
	
    try
    {
// Только зарегистрированные пользователи могут вносить данные
		if( !isset($_SESSION['user_id']) || $_SESSION['user_id'] == 0 )
			throw new Exception(ERR_M::TIMEOUT_ERROR, 999);
		$jtp_user_id = $_SESSION['user_id'];
		
//Есть ли разрешение на вставку записей
		$rwd = $_SESSION['priv']->jobtypes->rwd;
		if( ($rwd & PERM_BITS::WRITE_ANY) == 0 ) throw new Exception(ERR_M::DATA_OPER_DISABLED, 99);

// Все ли данные переданы
		$errdata = "";
		
// jtp_id
        if (!$jtp_id || !ctype_digit( $jtp_id ))
			throw new Exception(ERR_M::PROTOCOL_ERROR, 1);
        
// jtp_cat_id
		if( !$jtp_cat_id || !ctype_digit($jtp_cat_id) )
			$errdata .= ($errdata?'<br>':'')."- Категория работ не указана или указана неверно.";
		
// jtp_code
		if( $jtp_code == '' ) $errdata .= ($errdata?'<br>':'')."- Не указан шифр вида работ.";
		else {
			$jtp_code = mb_strtoupper( $jtp_code, 'UTF-8' );
			$jtp_code = preg_replace( '/[^А-ЯІЇA-Z0-9]/u', '', $jtp_code );
			$jtp_code = mb_substr( $jtp_code, 0, 16, 'UTF-8' );
		}

// jtp_descr
		if( $jtp_descr == '' ) $errdata .= ($errdata?'<br>':'')."- Не задано описание вида работ.";
		else $jtp_descr = mb_substr( $jtp_descr, 0, 128, 'UTF-8');

//				
// Если есть ошибки - можно уже закончить
//
		if( $errdata ) throw new Exception($errdata, 1);
		
// Открываем базу данных
		$db = App::me()->dbOpen();
		
// Проверяем задействована ли эта запись в других таблицах
		if( ( $errdata = $this->is_jtp_used_anyware( $db, $jtp_id )) != '' )
			throw new Exception( "Это описание вида работ уже задействовано в следующих справочниках:<br>".$errdata, 1);

// Есть ли разрешение на апдейт этой записи
		if( $rwd & PERM_BITS::WRITE_OTH ) $where = '1 = 1';
		elseif( $rwd & PERM_BITS::WRITE_GRP ) 
			$where = 'jtp_user_id IN (SELECT user_id FROM users WHERE cat_id = '.$_SESSION['cat_id'].')';
		elseif( $rwd & PERM_BITS::WRITE_OWN ) $where = 'jtp_user_id = '.$_SESSION['user_id'];
		else $where = '1 = 0';
		$where .= ' AND jtp_id = '.$db->quote($jtp_id);
		
		$sqlreq = 'SELECT COUNT(*) AS count FROM jobtypes WHERE '.$where.' LIMIT 1';
		$rs = $db->query( $sqlreq );		
		$row = $rs->fetch(PDO::FETCH_ASSOC);
		if( !$row['count'] ) {
			throw new Exception(ERR_M::DATA_OWNER_DISABLED, 99);
        }    
		$rs->closeCursor();
		
// Может быть шифр уже существует
		
		$sqlreq =	'SELECT jtp_code FROM jobtypes WHERE '.
					' jtp_code = '.$db->quote($jtp_code).
					' AND jtp_id != '.$db->quote($jtp_id);
		$rs = $db->query( $sqlreq );		
		$row = $rs->fetch(PDO::FETCH_ASSOC);
		if( $row ) {
			throw new Exception('В справочнике уже есть запись с таким шифром.', 1);
        }    
		$rs->closeCursor();

// Пишем данные
        $sqlreq =   'UPDATE jobtypes SET '.
						'jtp_cat_id = '.$db->quote($jtp_cat_id).", ".
						'jtp_code = '.$db->quote($jtp_code).", ".
						'jtp_descr = '.$db->quote($jtp_descr)." ".
					'WHERE jtp_id = '.$db->quote($jtp_id);
		$db->exec($sqlreq);

		$data->key = $jtp_id;
		$data->cat_id = $jtp_cat_id;
		$data->title = $jtp_code;
		$data->tooltip = $jtp_descr;
    }
	catch (PDOException $e) {
		$errcode = 1;
		$errmess = ERR_M::DATABASE_ERROR;
	}
    catch ( Exception $e ) {
		$errcode = $e->getCode();
		$errmess = $e->getMessage();
	}

	return array('errcode'=>$errcode,'errmess'=>$errmess, 'data'=>$data);
  }

  
  public function deljtp() {
    $errmess = "";
	$errcode = 0;
		
    $jtp_id = isset($_POST['jtp_id']) ? trim(strip_tags($_POST['jtp_id'])) : '';

    try
    {
// Только зарегистрированные пользователи могут удалять данные
		if( !isset($_SESSION['user_id']) || $_SESSION['user_id'] == 0 )
			throw new Exception(ERR_M::TIMEOUT_ERROR, 999);

        if (!$jtp_id || !ctype_digit( $jtp_id ))
			throw new Exception(ERR_M::PROTOCOL_ERROR, 1);
        
// Есть ли разрешение на удаление хоть каких либо записей jobtypes
		$rwd_jtp = $_SESSION['priv']->jobtypes->rwd;
		if( ($rwd_jtp & PERM_BITS::DELETE_ANY) == 0 ) throw new Exception(ERR_M::DATA_OPER_DISABLED, 99);
		
// Открываем базу данных
		$db = App::me()->dbOpen();	

// Проверяем задействована ли эта запись в других таблицах
		if( ( $errdata = $this->is_jtp_used_anyware( $db, $jtp_id )) != '' )
			throw new Exception( "Удаляемая запись задействована в следующих справочниках:<br>".$errdata, 1);

// Есть ли разрешение на удаление записей jobtypes
		if( $rwd_jtp & PERM_BITS::DELETE_OTH ) $where = '1 = 1';
		elseif( $rwd_jtp & PERM_BITS::DELETE_GRP ) 
			$where = 'jtp_user_id IN (SELECT user_id FROM users WHERE cat_id = '.$_SESSION['cat_id'].')';
		elseif( $rwd_jtp & PERM_BITS::DELETE_OWN ) $where = 'jtp_user_id = '.$_SESSION['user_id'];
		else $where = '1 = 0';
		$where .= ' AND jtp_id = '.$db->quote($jtp_id);
		
		$sqlreq = 'SELECT COUNT(*) AS count FROM jobtypes WHERE '.$where;
		$rs = $db->query( $sqlreq );		
		$row = $rs->fetch(PDO::FETCH_ASSOC);
		if( $row['count'] == 0 ) {
			throw new Exception(ERR_M::DATA_OWNER_DISABLED, 99);
        }    
		$rs->closeCursor();
		
// Удаляем записи из справочника
		$sql =	'DELETE FROM jobtypes WHERE jtp_id = '.$db->quote($jtp_id);
		$db->exec($sql);
	}
	catch (PDOException $e) {
		$errcode = 1;
		$errmess = ERR_M::DATABASE_ERROR;
	}
    catch ( Exception $e ) {
		$errcode = $e->getCode();
		$errmess = $e->getMessage();
	}

	return array('errcode'=>$errcode,'errmess'=>$errmess);
  }

  
  private function is_jtp_used_anyware( $db, $jtp_id ) {
	$sql =  "( SELECT 'Балансы по видам работ' as a FROM `baltypes` WHERE btp_jtp_id = ".$db->quote($jtp_id)." LIMIT 1 )".
			" UNION ".
			"( SELECT 'Приходы по видам работ' as a  FROM `inctypes` WHERE itp_jtp_id = ".$db->quote($jtp_id)." LIMIT 1 )".
			" UNION ".
			"( SELECT 'Расходы по видам работ' as a  FROM `outtypes` WHERE otp_jtp_id = ".$db->quote($jtp_id)." LIMIT 1 )";
	$errdata = '';
	$rs = $db->query($sql);
	while( $row = $rs->fetchObject() ) $errdata .= ($errdata?', ':'')."'".$row->a."'";
	$rs->closeCursor();	
	return $errdata;
  }

// ***************************************************
// Справочник видов баланса
//  
  
  public function listbtp() {
  
    $data = new jqGridData();
    try
    {
// Только зарегистрированные пользователи могут видеть данные
		if( !isset($_SESSION['user_id']) || $_SESSION['user_id'] == 0 )
			throw new Exception(ERR_M::TIMEOUT_ERROR, 999);

		$rwd = $_SESSION['priv']->baltypes->rwd;
		if( !$rwd  ) throw new Exception(ERR_M::DATA_ACC_DISABLED, 99);

// Проверяем данные
        $jtp_id = isset($_REQUEST["jtp_id"]) ? $_REQUEST["jtp_id"] : '';
		if( $jtp_id == '' || !ctype_digit($jtp_id) )
			throw new Exception(ERR_M::PROTOCOL_ERROR, 1);

        $page    = ( isset($_REQUEST["page"]) && $_REQUEST["page"] > 0 ) ? $_REQUEST["page"] : 1;
        $limit    = ( isset($_REQUEST["rows"]) && $_REQUEST["rows"] > 0 ) ? $_REQUEST["rows"] : 1;
        $sidx    = ( isset($_REQUEST["sidx"]) && $_REQUEST["sidx"] != '' ) ? $_REQUEST["sidx"] : 'btp_ord';
        $sord    = ( isset($_REQUEST["sord"]) && strtolower($_REQUEST["sord"]) == 'desc' ) ? 'DESC' : 'ASC';
        $search    = ( isset($_REQUEST["_search"]) && strtolower($_REQUEST["_search"]) == 'true') ? true : false;
 
// Открываем базу данных
		$db = App::me()->dbOpen();	

		switch( $sidx ) {
			case 'btp_ord': $sidx = 'btp_ord'; break;
			case 'btp_code': $sidx = 'btp_code'; break;
			case 'btp_descr': $sidx = 'btp_descr'; break;
		}
		
// Фильтр
        $whc = array();
		$whc[] = 'btp_jtp_id = '.$db->quote($jtp_id);
		
        if( $search )
        {
            if(isset($_REQUEST["btp_ord"]) && $_REQUEST["btp_ord"] != "") {
		    	$whc[] = "btp_ord = " . $db->quote($_REQUEST["btp_ord"]);
            }
			
            if(isset($_REQUEST["btp_code"]) && $_REQUEST["btp_code"] != "") {
		    	$whc[] = "btp_code LIKE " . $db->quote("%".$_REQUEST["btp_code"]."%");
            }
			
            if(isset($_REQUEST["btp_descr"]) && $_REQUEST["btp_descr"] != "") {
		    	$whc[] = "btp_descr LIKE " . $db->quote("%".$_REQUEST["btp_descr"]."%");
            }
		}

// Фильтр привилегий
		if( $rwd & PERM_BITS::READ_OTH ) $whc[] = '1 = 1';
		elseif( $rwd & PERM_BITS::READ_GRP ) 
			$whc[] = 'btp_user_id IN (SELECT user_id FROM users WHERE cat_id = '.$_SESSION['cat_id'].')';
		elseif( $rwd & PERM_BITS::READ_OWN ) $whc[] = 'btp_user_id = '.$_SESSION['user_id'];
		else $whc[] = '1 = 0';
		
        $whereclaus = (count($whc) != 0 ) ? " WHERE " . implode(" AND ", $whc) : "";
		unset($whc);
            
// Получаем количество записей с учетом фильтра           
        $sql =	"SELECT COUNT(*) AS count FROM baltypes ".$whereclaus;
        $rs =	$db->query( $sql );
		$row =	$rs->fetchObject();
		$count = $row->count;
		$rs->closeCursor();
            
// Пустая таблица
        if ($count == 0)
			throw new Exception(ERR_M::SUCCESS, 0);
            
// Рассчитаем сколько всего страниц займут данные в БД
    	if( $count > 0 && $limit > 0) {
    		$total_pages = ceil($count/$limit);
    	} else {
    		$total_pages = 0;
    	}                  
 
		if ($page > $total_pages) $page=$total_pages;                  
 
		$start = $limit*$page - $limit;                   
		if($start < 0) $start = 0;                  
 
// Формируем запрос
		$sql =	"SELECT btp_id, btp_ord, btp_code, btp_descr ".
				"FROM baltypes ".$whereclaus." ORDER BY ".$sidx." ".$sord.
		        " LIMIT ".$start.", ".$limit;

		$rs =	$db->query($sql);                  
 
// Формирование объекта для возврата
        
    	$data->page      = $page;
    	$data->total     = $total_pages;
    	$data->records   = $count;
    		
    // Строки данных для таблицы
		$i = 0;
		while($row = $rs->fetchObject()) {
		
			$data->rows[$i]['id'] = $row->btp_id;
			$data->rows[$i]['cell'] = array(
					$row->btp_id,
					$row->btp_ord,
					$row->btp_code,
					$row->btp_descr
		    );
		    $i++;
		}
    }
	catch (PDOException $e) {
		$data->userdata->errcode = 1;
		$data->userdata->errmess = ERR_M::DATABASE_ERROR;
	}
    catch ( Exception $e ) {
		$data->userdata->errcode = $e->getCode();
		$data->userdata->errmess = $e->getMessage();
	}
 
	return $data;
  }
  
  
  public function findbtp() {
	$errcode = 0;
	$errmess = '';
    $data = array();
	
    $term   = isset($_REQUEST["term"]) ? trim(strip_tags($_REQUEST["term"])) : '';
    $jtp_id   = isset($_REQUEST["jtp_id"]) ? trim(strip_tags($_REQUEST["jtp_id"])) : '';


    try
    {
// Только зарегистрированные пользователи могут видеть данные
		if( !isset($_SESSION['user_id']) || $_SESSION['user_id'] == 0 )
			throw new Exception(ERR_M::TIMEOUT_ERROR, 999);

// Find - услуга общего пользования. Поэтому, маска должна быть READ_ANY
		$rwd = $_SESSION['priv']->baltypes->rwd;
		if( ( $rwd & PERM_BITS::READ_ANY ) == 0 ) throw new Exception(ERR_M::DATA_OPER_DISABLED, 99);

// Проверяем данные
        if ($term == '' || !$jtp_id || !ctype_digit($jtp_id) )
			throw new Exception(ERR_M::PROTOCOL_ERROR, 1);
   
// Открываем базу данных
		$db = App::me()->dbOpen();	

// Фильтр привилегий
		if( $rwd & PERM_BITS::READ_OTH ) $whs = '1 = 1';
		elseif( $rwd & PERM_BITS::READ_GRP ) 
			$whs = 'btp_user_id IN (SELECT user_id FROM users WHERE cat_id = '.$_SESSION['cat_id'].')';
		elseif( $rwd & PERM_BITS::READ_OWN ) $whs = 'btp_user_id = '.$_SESSION['user_id'];
		else $whs = '1 = 0';
		
// Формируем запрос.
		$sql =  "SELECT btp_id, btp_code, btp_descr ".
				"FROM baltypes ".
				"WHERE btp_jtp_id = ".$db->quote($jtp_id)." AND ( btp_code LIKE ".$db->quote("%".$term."%").
				" OR btp_descr LIKE ".$db->quote("%".$term."%").' ) AND '.$whs." ORDER BY btp_ord";
					
		$rs = $db->query($sql);                  
        while( $row = $rs->fetchObject() ) {
            $item = new stdClass;
            $item->btp_id = $row->btp_id;
			
			$num = mb_strlen($term,'UTF-8');
			$pos = mb_stripos( $row->btp_code, $term, 0, 'UTF-8');
			if( !is_bool($pos) ) {
				$bterm = mb_substr($row->btp_code, $pos, $num, 'UTF-8');
				$name = preg_replace( '/'.$bterm.'/u', '<b>'.$bterm.'</b>', $row->btp_code );				
			} else $name = $row->btp_code;
			$pos = mb_stripos( $row->btp_descr, $term, 0, 'UTF-8');
			if( !is_bool($pos) ) {
				$bterm = mb_substr($row->btp_descr, $pos, $num, 'UTF-8');
				$full = preg_replace( '/'.$bterm.'/u', '<b>'.$bterm.'</b>', $row->btp_descr );				
			} else $full = $row->btp_descr;
			
            $item->btp_code = $name;
			$item->btp_descr = $full;
            $data[] = $item;
        }
        $rs->closeCursor();
 
    }
	catch (PDOException $e) {
		$errcode = 1;
		$errmess = ERR_M::DATABASE_ERROR;
	}
    catch ( Exception $e ) {
		$errcode = $e->getCode();
		$errmess = $e->getMessage();
	}
 
	return array('errcode'=>$errcode,'errmess'=>$errmess, 'data'=>$data);
  }
  
  
  public function insbtp() {

    $jtp_id		= isset($_POST['jtp_id']) ? trim(strip_tags($_POST['jtp_id'])) : '';
    $btp_ord	= isset($_POST['btp_ord']) ? trim(strip_tags($_POST['btp_ord'])) : '';
    $btp_code	= isset($_POST['btp_code']) ? trim(strip_tags($_POST['btp_code'])) : '';
    $btp_descr	= isset($_POST['btp_descr']) ? trim(strip_tags($_POST['btp_descr'])) : '';
	
	$errcode = 0;
	$errmess = '';
    try
    {
// Открываем базу данных
		$db = App::me()->dbOpen();	

// Только зарегистрированные пользователи могут вносить данные
		if( !isset($_SESSION['user_id']) || $_SESSION['user_id'] == 0 )
			throw new Exception(ERR_M::TIMEOUT_ERROR, 999);
		$btp_user_id = $_SESSION['user_id'];
		
		if( !$jtp_id || !ctype_digit($jtp_id) )
			throw new Exception(ERR_M::PROTOCOL_ERROR, 1);
			
//Есть ли разрешение на вставку записей
		$rwd = $_SESSION['priv']->baltypes->rwd;
		if( ($rwd & PERM_BITS::WRITE_OWN) == 0 ) throw new Exception(ERR_M::DATA_OPER_DISABLED, 99);

// Все ли данные переданы
		$errdata = "";
		
// btp_ord			
		if( !$btp_ord || !ctype_digit($btp_ord)  ) 
			$errdata .= ($errdata?'<br>':'').'- Не указан или неправильно указан порядковый номер.';
			
// btp_code			
		if( !$btp_code  ) $errdata .= ($errdata?'<br>':'').'- Не указан шифр баланса.';
		else {
//			$btp_code = mb_strtoupper( $btp_code, 'UTF-8' );
//			$btp_code = preg_replace( '/[^А-ЯІЇA-Z0-9]/u', '', $btp_code );
			$btp_code = mb_substr( $btp_code, 0, 16, 'UTF-8' );
		}

// btp_descr
		if( !$btp_descr ) $errdata .= ($errdata?'<br>':'').'- Не задана расшифровка вида баланса.';
		else $btp_descr = mb_substr( $btp_descr, 0, 128, 'UTF-8');

//				
// Если есть ошибки - можно уже закончить
//
		if( $errdata ) throw new Exception($errdata, 1);
		
// Может этот вид баланса уже есть
		$sql =	'SELECT count(*) AS count FROM baltypes WHERE btp_code = '.$db->quote($btp_code).
					' AND btp_jtp_id = '.$db->quote($jtp_id);
		$rs = $db->query( $sql );		
		$row = $rs->fetchObject();
		if( $row->count != 0 ) {
			throw new Exception('В справочнике уже присутствует запись с шифром "'.$btp_code.'"', 1);
        }    
		$rs->closeCursor();

// Пишем данные
        $sql =  'INSERT baltypes SET '.
					'btp_jtp_id = '.$db->quote($jtp_id)		.", ".
					'btp_ord = '  .$db->quote($btp_ord)	.", ".
					'btp_code = '  .$db->quote($btp_code)	.", ".
					'btp_descr = ' .$db->quote($btp_descr)	.", ".
					'btp_user_id = ' .$db->quote($btp_user_id);
												
		$db->exec($sql);
    }
	catch (PDOException $e) {
		$errcode = 1;
		$errmess = ERR_M::DATABASE_ERROR;
	}
    catch ( Exception $e ) {
		$errcode = $e->getCode();
		$errmess = $e->getMessage();
	}

	return array('errcode'=>$errcode,'errmess'=>$errmess);
  }

  public function updbtp() {

    $btp_id		= isset($_POST['id']) ? trim(strip_tags($_POST['id'])) : '';
    $jtp_id		= isset($_POST['jtp_id']) ? trim(strip_tags($_POST['jtp_id'])) : '';
    $btp_ord	= isset($_POST['btp_ord']) ? trim(strip_tags($_POST['btp_ord'])) : '';
    $btp_code	= isset($_POST['btp_code']) ? trim(strip_tags($_POST['btp_code'])) : '';
    $btp_descr	= isset($_POST['btp_descr']) ? trim(strip_tags($_POST['btp_descr'])) : '';
	
	$errcode = 0;
	$errmess = '';
    try
    {
// Открываем базу данных
		$db = App::me()->dbOpen();	

// Только зарегистрированные пользователи могут вносить данные
		if( !isset($_SESSION['user_id']) || $_SESSION['user_id'] == 0 )
			throw new Exception(ERR_M::TIMEOUT_ERROR, 999);
		$btp_user_id = $_SESSION['user_id'];
		
		if( !$jtp_id || !ctype_digit($jtp_id) || !$btp_id || !ctype_digit($btp_id) )
			throw new Exception(ERR_M::PROTOCOL_ERROR, 1);
			
//Есть ли разрешение на вставку записей
		$rwd = $_SESSION['priv']->baltypes->rwd;
		if( ($rwd & PERM_BITS::WRITE_ANY) == 0 ) throw new Exception(ERR_M::DATA_OPER_DISABLED, 99);

// Все ли данные переданы
		$errdata = "";
		
// btp_ord			
		if( !$btp_ord || !ctype_digit($btp_ord)  ) 
			$errdata .= ($errdata?'<br>':'').'- Не указан или неправильно указан порядковый номер.';
			
// btp_code			
		if( !$btp_code  ) $errdata .= ($errdata?'<br>':'').'- Не указан шифр баланса.';
		else {
//			$btp_code = mb_strtoupper( $btp_code, 'UTF-8' );
//			$btp_code = preg_replace( '/[^А-ЯІЇA-Z0-9]/u', '', $btp_code );
			$btp_code = mb_substr( $btp_code, 0, 16, 'UTF-8' );
		}

// btp_descr
		if( !$btp_descr ) $errdata .= ($errdata?'<br>':'').'- Не задана расшифровка вида баланса.';
		else $btp_descr = mb_substr( $btp_descr, 0, 128, 'UTF-8');

//				
// Если есть ошибки - можно уже закончить
//
		if( $errdata ) throw new Exception($errdata, 1);
		
// Проверяем задействована ли эта запись в других таблицах
		if( ( $errdata = $this->is_btp_used_anyware( $db, $btp_id )) != '' )
			throw new Exception( "Этот вид баланса уже задействован в следующих справочниках:<br>".$errdata, 1);

// Есть ли разрешение на апдейт этой записи
		if( $rwd & PERM_BITS::WRITE_OTH ) $where = '1 = 1';
		elseif( $rwd & PERM_BITS::WRITE_GRP ) 
			$where = 'btp_user_id IN (SELECT user_id FROM users WHERE cat_id = '.$_SESSION['cat_id'].')';
		elseif( $rwd & PERM_BITS::WRITE_OWN ) $where = 'btp_user_id = '.$_SESSION['user_id'];
		else $where = '1 = 0';
		$where .= ' AND btp_id = '.$db->quote($btp_id);
		
		$sql = 'SELECT btp_code FROM baltypes WHERE '.$where.' LIMIT 1';
		$rs = $db->query( $sql );		
		$row = $rs->fetchObject();
		if( !$row ) {
			throw new Exception(ERR_M::DATA_OWNER_DISABLED, 99);
        }
		$rs->closeCursor();
		
// Может этот вид баланса уже есть
		$sql =	'SELECT count(*) AS count FROM baltypes WHERE btp_code = '.$db->quote($btp_code).
					' AND btp_jtp_id = '.$db->quote($jtp_id).' AND btp_id != '.$db->quote($btp_id);
		$rs = $db->query( $sql );		
		$row = $rs->fetchObject();
		if( $row->count != 0 ) {
			throw new Exception('В справочнике уже присутствует запись с шифром "'.$btp_code.'"', 1);
        }    
		$rs->closeCursor();

// Пишем данные
        $sql =  'UPDATE baltypes SET '.
					'btp_ord = ' .$db->quote($btp_ord).", ".
					'btp_code = ' .$db->quote($btp_code).", ".
					'btp_descr = '.$db->quote($btp_descr)." ".
				'WHERE btp_id = '.$db->quote($btp_id);
					
		$db->exec($sql);
    }
	catch (PDOException $e) {
		$errcode = 1;
		$errmess = ERR_M::DATABASE_ERROR;
	}
    catch ( Exception $e ) {
		$errcode = $e->getCode();
		$errmess = $e->getMessage();
	}

	return array('errcode'=>$errcode,'errmess'=>$errmess);
  }

  
  public function delbtp() {
    $errmess = "";
	$errcode = 0;
		
    $btp_ids = (isset($_REQUEST['id']) && $_REQUEST['id'] != '') ?
        explode(",", $_REQUEST["id"], 100) : array();

    try
    {
// Только зарегистрированные пользователи могут удалять данные
		if( !isset($_SESSION['user_id']) || $_SESSION['user_id'] == 0 )
			throw new Exception(ERR_M::TIMEOUT_ERROR, 999);

// Есть ли разрешение на удаление хоть какой либо записи
		$rwd = $_SESSION['priv']->baltypes->rwd;
		if( ($rwd & PERM_BITS::DELETE_ANY) == 0 ) throw new Exception(ERR_M::DATA_OPER_DISABLED, 99);
		
        if (!Tools::is_numeric_array( $btp_ids ))
			throw new Exception(ERR_M::PROTOCOL_ERROR, 1);
        
		$btp_id_list = implode(',', $btp_ids);

// Открываем базу данных
		$db = App::me()->dbOpen();	

// Проверяем задействованы ли эти записи в других таблицах
		if( ( $errdata = $this->is_btp_used_anyware( $db, $btp_id_list )) != '' )
			throw new Exception( "В списке удаляемых записей присутствует запись(си) уже задействованная в следующих справочниках:<br>".$errdata, 1);

// Есть ли разрешение на удаление этих записей
		if( $rwd & PERM_BITS::DELETE_OTH ) $where = '1 = 1';
		elseif( $rwd & PERM_BITS::DELETE_GRP ) 
			$where = 'btp_user_id IN (SELECT user_id FROM users WHERE cat_id = '.$_SESSION['cat_id'].')';
		elseif( $rwd & PERM_BITS::DELETE_OWN ) $where = 'btp_user_id = '.$_SESSION['user_id'];
		else $where = '1 = 0';
		$where .= ' AND btp_id IN ('.$btp_id_list.')';
		
		$sql = 'SELECT COUNT(*) AS count FROM baltypes WHERE '.$where;
		$rs = $db->query( $sql );		
		$row = $rs->fetchObject();
		if( $row->count != count($btp_ids) ) {
			throw new Exception(ERR_M::DATA_OWNER_DISABLED, 99);
        }    
		$rs->closeCursor();
		
// Удаляем записи из справочника
        $sqlreq = "DELETE FROM baltypes WHERE btp_id IN (".$btp_id_list.")";
        $db->exec($sqlreq);
	}
	catch (PDOException $e) {
		$errcode = 1;
		$errmess = ERR_M::DATABASE_ERROR;
	}
    catch ( Exception $e ) {
		$errcode = $e->getCode();
		$errmess = $e->getMessage();
	}

	return array('errcode'=>$errcode,'errmess'=>$errmess);
  }
  
  
  private function is_btp_used_anyware( $db, $btp_id_list ) {
	$sql =	"( SELECT 'Виды прихода' as a FROM `inctypes` WHERE itp_btp_id IN ( ${btp_id_list} ) LIMIT 1 )".
			" UNION ALL ".
			"( SELECT 'Виды расходов' as a FROM `outtypes` WHERE otp_btp_id IN ( ${btp_id_list} ) LIMIT 1 )";
	$errdata = '';
	$rs = $db->query($sql);
	while( $row = $rs->fetchObject() ) $errdata .= ($errdata?', ':'')."'".$row->a."'";
	$rs->closeCursor();
	return $errdata;
  }


// ***************************************************
// Справочник видов входящих платежей
//  
  
  public function listitp() {
  
    $data = new jqGridData();
    try
    {
// Только зарегистрированные пользователи могут видеть данные
		if( !isset($_SESSION['user_id']) || $_SESSION['user_id'] == 0 )
			throw new Exception(ERR_M::TIMEOUT_ERROR, 999);

		$rwd = $_SESSION['priv']->inctypes->rwd;
		if( !$rwd  ) throw new Exception(ERR_M::DATA_ACC_DISABLED, 99);

// Проверяем данные
        $jtp_id = isset($_REQUEST["jtp_id"]) ? $_REQUEST["jtp_id"] : '';
		if( $jtp_id == '' || !ctype_digit($jtp_id) )
			throw new Exception(ERR_M::PROTOCOL_ERROR, 1);

        $page    = ( isset($_REQUEST["page"]) && $_REQUEST["page"] > 0 ) ? $_REQUEST["page"] : 1;
        $limit    = ( isset($_REQUEST["rows"]) && $_REQUEST["rows"] > 0 ) ? $_REQUEST["rows"] : 1;
        $sidx    = ( isset($_REQUEST["sidx"]) && $_REQUEST["sidx"] != '' ) ? $_REQUEST["sidx"] : 'itp_ord';
        $sord    = ( isset($_REQUEST["sord"]) && strtolower($_REQUEST["sord"]) == 'desc' ) ? 'DESC' : 'ASC';
        $search    = ( isset($_REQUEST["_search"]) && strtolower($_REQUEST["_search"]) == 'true') ? true : false;
 
// Открываем базу данных
		$db = App::me()->dbOpen();	

		switch( $sidx ) {
			case 'itp_ord': $sidx = 'inc.itp_ord'; break;
			case 'itp_code': $sidx = 'inc.itp_code'; break;
			case 'itp_descr': $sidx = 'inc.itp_descr'; break;
			case 'btp_code': $sidx = 'bal.btp_code'; break;
		}
		
// Фильтр
        $whc = array();
		$whc[] = 'itp_jtp_id = '.$db->quote($jtp_id);
		
        if( $search )
        {
            if(isset($_REQUEST["itp_ord"]) && $_REQUEST["itp_ord"] != "") {
		    	$whc[] = "inc.itp_ord = " . $db->quote($_REQUEST["itp_ord"]);
            }
			
            if(isset($_REQUEST["itp_code"]) && $_REQUEST["itp_code"] != "") {
		    	$whc[] = "inc.itp_code LIKE " . $db->quote("%".$_REQUEST["itp_code"]."%");
            }
			
            if(isset($_REQUEST["itp_descr"]) && $_REQUEST["itp_descr"] != "") {
		    	$whc[] = "inc.itp_descr LIKE " . $db->quote("%".$_REQUEST["itp_descr"]."%");
            }
            if(isset($_REQUEST["btp_code"]) && $_REQUEST["btp_code"] != "") {
		    	$whc[] = "bal.btp_code LIKE " . $db->quote("%".$_REQUEST["btp_code"]."%");
            }
			
		}

// Фильтр привилегий
		if( $rwd & PERM_BITS::READ_OTH ) $whc[] = '1 = 1';
		elseif( $rwd & PERM_BITS::READ_GRP ) 
			$whc[] = 'inc.itp_user_id IN (SELECT user_id FROM users WHERE cat_id = '.$_SESSION['cat_id'].')';
		elseif( $rwd & PERM_BITS::READ_OWN ) $whc[] = 'inc.itp_user_id = '.$_SESSION['user_id'];
		else $whc[] = '1 = 0';
		
        $whereclaus = (count($whc) != 0 ) ? " WHERE " . implode(" AND ", $whc) : "";
		unset($whc);
            
// JOINS
		$joins = "LEFT JOIN baltypes AS bal ON bal.btp_id = inc.itp_btp_id ";
		
// Получаем количество записей с учетом фильтра           
        $sql =	"SELECT COUNT(*) AS count FROM inctypes AS inc ".$joins.$whereclaus;
        $rs =	$db->query( $sql );
		$row =	$rs->fetchObject();
		$count = $row->count;
		$rs->closeCursor();
            
// Пустая таблица
        if ($count == 0)
			throw new Exception(ERR_M::SUCCESS, 0);
            
// Рассчитаем сколько всего страниц займут данные в БД
    	if( $count > 0 && $limit > 0) {
    		$total_pages = ceil($count/$limit);
    	} else {
    		$total_pages = 0;
    	}                  
 
		if ($page > $total_pages) $page=$total_pages;                  
 
		$start = $limit*$page - $limit;                   
		if($start < 0) $start = 0;                  
 
// Формируем запрос
		$sql =	"SELECT itp_id, itp_ord, itp_code, itp_descr, itp_btp_id AS btp_id, bal.btp_code AS btp_code ".
				"FROM inctypes AS inc ".$joins.$whereclaus." ORDER BY ".$sidx." ".$sord.
		        " LIMIT ".$start.", ".$limit;

		$rs =	$db->query($sql);                  
 
// Формирование объекта для возврата
        
    	$data->page      = $page;
    	$data->total     = $total_pages;
    	$data->records   = $count;
    		
    // Строки данных для таблицы
		$i = 0;
		while($row = $rs->fetchObject()) {
		
			$data->rows[$i]['id'] = $row->itp_id;
			$data->rows[$i]['cell'] = array(
					$row->itp_id,
					$row->itp_ord,
					$row->itp_code,
					$row->itp_descr,
					$row->btp_id,
					$row->btp_code
		    );
		    $i++;
		}
    }
	catch (PDOException $e) {
		$data->userdata->errcode = 1;
		$data->userdata->errmess = ERR_M::DATABASE_ERROR;
	}
    catch ( Exception $e ) {
		$data->userdata->errcode = $e->getCode();
		$data->userdata->errmess = $e->getMessage();
	}
 
	return $data;
  }
  
  
  public function finditp() {
	$errcode = 0;
	$errmess = '';
    $data = array();
	
    $term   = isset($_REQUEST["term"]) ? trim(strip_tags($_REQUEST["term"])) : '';
    $jtp_id   = isset($_REQUEST["jtp_id"]) ? trim(strip_tags($_REQUEST["jtp_id"])) : '';


    try
    {
// Только зарегистрированные пользователи могут видеть данные
		if( !isset($_SESSION['user_id']) || $_SESSION['user_id'] == 0 )
			throw new Exception(ERR_M::TIMEOUT_ERROR, 999);

// Маска должна быть READ_ANY
		$rwd = $_SESSION['priv']->inctypes->rwd;
		if( ( $rwd & PERM_BITS::READ_ANY ) == 0 ) throw new Exception(ERR_M::DATA_OPER_DISABLED, 99);

// Проверяем данные
        if ($term == '' || !$jtp_id || !ctype_digit($jtp_id) )
			throw new Exception(ERR_M::PROTOCOL_ERROR, 1);
   
// Открываем базу данных
		$db = App::me()->dbOpen();	

// Фильтр привилегий
		if( $rwd & PERM_BITS::READ_OTH ) $whs = '1 = 1';
		elseif( $rwd & PERM_BITS::READ_GRP ) 
			$whs = 'itp_user_id IN (SELECT user_id FROM users WHERE cat_id = '.$_SESSION['cat_id'].')';
		elseif( $rwd & PERM_BITS::READ_OWN ) $whs = 'itp_user_id = '.$_SESSION['user_id'];
		else $whs = '1 = 0';
		
// Формируем запрос.
		$sql =  "SELECT itp_id, itp_code, itp_descr ".
				"FROM inctypes ".
				"WHERE itp_jtp_id = ".$db->quote($jtp_id)." AND ( itp_code LIKE ".$db->quote("%".$term."%").
				" OR itp_descr LIKE ".$db->quote("%".$term."%").' ) AND '.$whs." ORDER BY itp_ord";
					
		$rs = $db->query($sql);                  
        while( $row = $rs->fetchObject() ) {
            $item = new stdClass;
            $item->itp_id = $row->itp_id;
			
			$num = mb_strlen($term,'UTF-8');
			$pos = mb_stripos( $row->itp_code, $term, 0, 'UTF-8');
			if( !is_bool($pos) ) {
				$bterm = mb_substr($row->itp_code, $pos, $num, 'UTF-8');
				$name = preg_replace( '/'.$bterm.'/u', '<b>'.$bterm.'</b>', $row->itp_code );				
			} else $name = $row->itp_code;
			$pos = mb_stripos( $row->itp_descr, $term, 0, 'UTF-8');
			if( !is_bool($pos) ) {
				$bterm = mb_substr($row->itp_descr, $pos, $num, 'UTF-8');
				$full = preg_replace( '/'.$bterm.'/u', '<b>'.$bterm.'</b>', $row->itp_descr );				
			} else $full = $row->itp_descr;
			
            $item->itp_code = $name;
			$item->itp_descr = $full;
            $data[] = $item;
        }
        $rs->closeCursor();
 
    }
	catch (PDOException $e) {
		$errcode = 1;
		$errmess = ERR_M::DATABASE_ERROR;
	}
    catch ( Exception $e ) {
		$errcode = $e->getCode();
		$errmess = $e->getMessage();
	}
 
	return array('errcode'=>$errcode,'errmess'=>$errmess, 'data'=>$data);
  }
  
  
  public function findallitp() {
	$errcode = 0;
	$errmess = '';
    $data = array();
	
    $term   = isset($_REQUEST["term"]) ? trim(strip_tags($_REQUEST["term"])) : '';

    try
    {
// Только зарегистрированные пользователи могут видеть данные
		if( !isset($_SESSION['user_id']) || $_SESSION['user_id'] == 0 )
			throw new Exception(ERR_M::TIMEOUT_ERROR, 999);

// Маска должна быть READ_ANY
		$rwd = $_SESSION['priv']->inctypes->rwd;
		if( ( $rwd & PERM_BITS::READ_ANY ) == 0 ) throw new Exception(ERR_M::DATA_OPER_DISABLED, 99);

// Проверяем данные
        if ($term == '' ) throw new Exception(ERR_M::PROTOCOL_ERROR, 1);
   
        $exactly   = (isset($_REQUEST["exactly"]) && strtolower($_REQUEST["exactly"]) == 'true' ) ? true : false;

// Открываем базу данных
		$db = App::me()->dbOpen();	

// Фильтр привилегий
		if( $rwd & PERM_BITS::READ_OTH ) $whs = '1 = 1';
		elseif( $rwd & PERM_BITS::READ_GRP ) 
			$whs = 'itp_user_id IN (SELECT user_id FROM users WHERE cat_id = '.$_SESSION['cat_id'].')';
		elseif( $rwd & PERM_BITS::READ_OWN ) $whs = 'itp_user_id = '.$_SESSION['user_id'];
		else $whs = '1 = 0';
		
// Формируем запрос.
		if( $exactly )
			$sql =  "SELECT itp_id, itp_code, itp_descr ".
					"FROM inctypes ".
					"WHERE itp_code = ".$db->quote($term).' AND '.$whs." ORDER BY itp_jtp_id, itp_ord";
		else
			$sql =  "SELECT itp_id, itp_code, itp_descr ".
					"FROM inctypes ".
					"WHERE ( itp_code LIKE ".$db->quote("%".$term."%").
					" OR itp_descr LIKE ".$db->quote("%".$term."%").' ) AND '.$whs." ORDER BY itp_jtp_id, itp_ord";
		
		$rs = $db->query($sql);                  
        while( $row = $rs->fetchObject() ) {
            $item = new stdClass;
            $item->itp_id = $row->itp_id;
			
			$num = mb_strlen($term,'UTF-8');
			$pos = mb_stripos( $row->itp_code, $term, 0, 'UTF-8');
			if( !is_bool($pos) ) {
				$bterm = mb_substr($row->itp_code, $pos, $num, 'UTF-8');
				$name = preg_replace( '/'.$bterm.'/u', '<b>'.$bterm.'</b>', $row->itp_code );				
			} else $name = $row->itp_code;
			$pos = mb_stripos( $row->itp_descr, $term, 0, 'UTF-8');
			if( !is_bool($pos) ) {
				$bterm = mb_substr($row->itp_descr, $pos, $num, 'UTF-8');
				$full = preg_replace( '/'.$bterm.'/u', '<b>'.$bterm.'</b>', $row->itp_descr );				
			} else $full = $row->itp_descr;
			
            $item->itp_code = $name;
			$item->itp_descr = $full;
            $data[] = $item;
        }
        $rs->closeCursor();
 
    }
	catch (PDOException $e) {
		$errcode = 1;
		$errmess = ERR_M::DATABASE_ERROR;
	}
    catch ( Exception $e ) {
		$errcode = $e->getCode();
		$errmess = $e->getMessage();
	}
 
	return array('errcode'=>$errcode,'errmess'=>$errmess, 'data'=>$data);
  }
  
  
  public function insitp() {

    $jtp_id		= isset($_POST['jtp_id']) ? trim(strip_tags($_POST['jtp_id'])) : '';
    $itp_ord	= isset($_POST['itp_ord']) ? trim(strip_tags($_POST['itp_ord'])) : '';
    $itp_code	= isset($_POST['itp_code']) ? trim(strip_tags($_POST['itp_code'])) : '';
    $itp_descr	= isset($_POST['itp_descr']) ? trim(strip_tags($_POST['itp_descr'])) : '';
    $btp_id		= isset($_POST['btp_id']) ? trim(strip_tags($_POST['btp_id'])) : '';
    $btp_code	= isset($_POST['btp_code']) ? trim(strip_tags($_POST['btp_code'])) : '';

	$errcode = 0;
	$errmess = '';
    try
    {
// Открываем базу данных
		$db = App::me()->dbOpen();	

// Только зарегистрированные пользователи могут вносить данные
		if( !isset($_SESSION['user_id']) || $_SESSION['user_id'] == 0 )
			throw new Exception(ERR_M::TIMEOUT_ERROR, 999);
		$itp_user_id = $_SESSION['user_id'];
		
		if( !$jtp_id || !ctype_digit($jtp_id) || !$btp_id || !ctype_digit($btp_id) )
			throw new Exception(ERR_M::PROTOCOL_ERROR, 1);
			
//Есть ли разрешение на вставку записей
		$rwd = $_SESSION['priv']->inctypes->rwd;
		if( ($rwd & PERM_BITS::WRITE_OWN) == 0 ) throw new Exception(ERR_M::DATA_OPER_DISABLED, 99);

// Все ли данные переданы
		$errdata = "";
		
// itp_ord			
		if( !$itp_ord || !ctype_digit($itp_ord) ) 
			$errdata .= ($errdata?'<br>':'').'- Не указан или указан неправильно порядковый номер.';
			
// itp_code			
		if( !$itp_code  ) $errdata .= ($errdata?'<br>':'').'- Не указан шифр прихода.';
		else {
//			$itp_code = mb_strtoupper( $itp_code, 'UTF-8' );
//			$itp_code = preg_replace( '/[^А-ЯІЇA-Z0-9]/u', '', $itp_code );
			$itp_code = mb_substr( $itp_code, 0, 16, 'UTF-8' );
		}

// itp_descr
		if( !$itp_descr ) $errdata .= ($errdata?'<br>':'').'- Не задана расшифровка вида прихода.';
		else $itp_descr = mb_substr( $itp_descr, 0, 128, 'UTF-8');

// Есть ли такой код баланса и соотв. ли он шифру
		$rs = $db->query( 'SELECT btp_code FROM baltypes WHERE btp_id = '.$db->quote($btp_id).
							' AND btp_jtp_id = '.$db->quote($jtp_id).' LIMIT 1' );		
		$row = $rs->fetchObject();
		if( !$row || $row->btp_code != $btp_code )
			$errdata .= ($errdata?'<br>':'').'- Неправильно задан код баланса.';
		$rs->closeCursor();

//				
// Если есть ошибки - можно уже закончить
//
		if( $errdata ) throw new Exception($errdata, 1);
		
// Может этот вид прихода уже есть
		$rs = $db->query( "SELECT '1' FROM inctypes WHERE itp_code = ".$db->quote($itp_code).
					" AND itp_jtp_id = ".$db->quote($jtp_id)." LIMIT 1" );
		if( $rs->fetchObject() )
			throw new Exception('В справочнике уже присутствует запись с шифром "'.$itp_code.'"', 1);
		$rs->closeCursor();

// Пишем данные
        $sql =  'INSERT inctypes SET '.
					'itp_jtp_id = '.$db->quote($jtp_id)		.", ".
					'itp_ord = '  .$db->quote($itp_ord)	.", ".
					'itp_code = '  .$db->quote($itp_code)	.", ".
					'itp_descr = ' .$db->quote($itp_descr)	.", ".
					'itp_btp_id = '.$db->quote($btp_id)		.", ".
					'itp_user_id = ' .$db->quote($itp_user_id);
												
		$db->exec($sql);
    }
	catch (PDOException $e) {
		$errcode = 1;
		$errmess = ERR_M::DATABASE_ERROR;
	}
    catch ( Exception $e ) {
		$errcode = $e->getCode();
		$errmess = $e->getMessage();
	}

	return array('errcode'=>$errcode,'errmess'=>$errmess);
  }

  public function upditp() {

    $jtp_id		= isset($_POST['jtp_id']) ? trim(strip_tags($_POST['jtp_id'])) : '';
    $itp_id		= isset($_POST['id']) ? trim(strip_tags($_POST['id'])) : '';
    $itp_ord	= isset($_POST['itp_ord']) ? trim(strip_tags($_POST['itp_ord'])) : '';
    $itp_code	= isset($_POST['itp_code']) ? trim(strip_tags($_POST['itp_code'])) : '';
    $btp_id		= isset($_POST['btp_id']) ? trim(strip_tags($_POST['btp_id'])) : '';
    $btp_code	= isset($_POST['btp_code']) ? trim(strip_tags($_POST['btp_code'])) : '';
    $itp_descr	= isset($_POST['itp_descr']) ? trim(strip_tags($_POST['itp_descr'])) : '';
	
	$errcode = 0;
	$errmess = '';
    try
    {
// Открываем базу данных
		$db = App::me()->dbOpen();	

// Только зарегистрированные пользователи могут вносить данные
		if( !isset($_SESSION['user_id']) || $_SESSION['user_id'] == 0 )
			throw new Exception(ERR_M::TIMEOUT_ERROR, 999);
		$itp_user_id = $_SESSION['user_id'];
		
		if( !$jtp_id || !ctype_digit($jtp_id) || !$btp_id || !ctype_digit($btp_id) || !$itp_id || !ctype_digit($itp_id) )
			throw new Exception(ERR_M::PROTOCOL_ERROR, 1);
			
//Есть ли разрешение на вставку записей
		$rwd = $_SESSION['priv']->inctypes->rwd;
		if( ($rwd & PERM_BITS::WRITE_ANY) == 0 ) throw new Exception(ERR_M::DATA_OPER_DISABLED, 99);

// Все ли данные переданы
		$errdata = "";
		
// itp_ord			
		if( !$itp_ord || !ctype_digit($itp_ord) ) 
			$errdata .= ($errdata?'<br>':'').'- Не указан или указан неправильно порядковый номер.';
			
// itp_code			
		if( !$itp_code  ) $errdata .= ($errdata?'<br>':'').'- Не указан шифр прихода.';
		else {
//			$itp_code = mb_strtoupper( $itp_code, 'UTF-8' );
//			$itp_code = preg_replace( '/[^А-ЯІЇA-Z0-9]/u', '', $itp_code );
			$itp_code = mb_substr( $itp_code, 0, 16, 'UTF-8' );
		}

// itp_descr
		if( !$itp_descr ) $errdata .= ($errdata?'<br>':'').'- Не задана расшифровка вида прихода.';
		else $itp_descr = mb_substr( $itp_descr, 0, 128, 'UTF-8');

// Есть ли такой код баланса и соотв. ли он шифру
		$rs = $db->query( 'SELECT btp_code FROM baltypes WHERE btp_id = '.$db->quote($btp_id).
							' AND btp_jtp_id = '.$db->quote($jtp_id).' LIMIT 1' );		
		$row = $rs->fetchObject();
		if( !$row || $row->btp_code != $btp_code )
			$errdata .= ($errdata?'<br>':'').'- Неправильно задан код баланса.';
		$rs->closeCursor();

//				
// Если есть ошибки - можно уже закончить
//
		if( $errdata ) throw new Exception($errdata, 1);
		
// Проверяем задействована ли эта запись в других таблицах
		if( ( $errdata = $this->is_itp_used_anyware( $db, $itp_id )) != '' )
			throw new Exception( "Этот вид прихода уже задействован в следующих документах:<br>".$errdata, 1);

// Есть ли разрешение на апдейт этой записи
		if( $rwd & PERM_BITS::WRITE_OTH ) $where = '1 = 1';
		elseif( $rwd & PERM_BITS::WRITE_GRP ) 
			$where = 'itp_user_id IN (SELECT user_id FROM users WHERE cat_id = '.$_SESSION['cat_id'].')';
		elseif( $rwd & PERM_BITS::WRITE_OWN ) $where = 'itp_user_id = '.$_SESSION['user_id'];
		else $where = '1 = 0';
		$where .= ' AND itp_id = '.$db->quote($itp_id);
		
		$rs = $db->query( 'SELECT itp_code FROM inctypes WHERE '.$where.' LIMIT 1' );
		if( !$rs->fetchObject() )
			throw new Exception(ERR_M::DATA_OWNER_DISABLED, 99);
		$rs->closeCursor();
		
// Может этот вид прихода уже есть
		$rs = $db->query( "SELECT '1' FROM inctypes WHERE itp_code = ".$db->quote($itp_code).
					" AND itp_jtp_id = ".$db->quote($jtp_id)." AND itp_id != ".$db->quote($itp_id)." LIMIT 1" );
		if( $rs->fetchObject() )
			throw new Exception('В справочнике уже присутствует запись с шифром "'.$itp_code.'"', 1);
		$rs->closeCursor();

// Пишем данные
        $sql =  'UPDATE inctypes SET '.
					'itp_ord = ' .$db->quote($itp_ord).", ".
					'itp_code = ' .$db->quote($itp_code).", ".
					'itp_btp_id = ' .$db->quote($btp_id).", ".
					'itp_descr = '.$db->quote($itp_descr)." ".
				'WHERE itp_id = '.$db->quote($itp_id);
					
		$db->exec($sql);
    }
	catch (PDOException $e) {
		$errcode = 1;
		$errmess = ERR_M::DATABASE_ERROR;
	}
    catch ( Exception $e ) {
		$errcode = $e->getCode();
		$errmess = $e->getMessage();
	}

	return array('errcode'=>$errcode,'errmess'=>$errmess);
  }

  
  public function delitp() {
    $errmess = "";
	$errcode = 0;
		
    $itp_ids = (isset($_REQUEST['id']) && $_REQUEST['id'] != '') ?
        explode(",", $_REQUEST["id"], 100) : array();

    try
    {
// Только зарегистрированные пользователи могут удалять данные
		if( !isset($_SESSION['user_id']) || $_SESSION['user_id'] == 0 )
			throw new Exception(ERR_M::TIMEOUT_ERROR, 999);

// Есть ли разрешение на удаление хоть какой либо записи
		$rwd = $_SESSION['priv']->inctypes->rwd;
		if( ($rwd & PERM_BITS::DELETE_ANY) == 0 ) throw new Exception(ERR_M::DATA_OPER_DISABLED, 99);
		
        if (!Tools::is_numeric_array( $itp_ids ))
			throw new Exception(ERR_M::PROTOCOL_ERROR, 1);
        
		$itp_id_list = implode(',', $itp_ids);

// Открываем базу данных
		$db = App::me()->dbOpen();	

// Проверяем задействованы ли эти записи в других таблицах
		if( ( $errdata = $this->is_itp_used_anyware( $db, $itp_id_list )) != '' )
			throw new Exception( "В списке удаляемых записей присутствует запись(си) уже задействованная в следующих документах:<br>".$errdata, 1);

// Есть ли разрешение на удаление этих записей
		if( $rwd & PERM_BITS::DELETE_OTH ) $where = '1 = 1';
		elseif( $rwd & PERM_BITS::DELETE_GRP ) 
			$where = 'itp_user_id IN (SELECT user_id FROM users WHERE cat_id = '.$_SESSION['cat_id'].')';
		elseif( $rwd & PERM_BITS::DELETE_OWN ) $where = 'btp_user_id = '.$_SESSION['user_id'];
		else $where = '1 = 0';
		$where .= ' AND itp_id IN ('.$itp_id_list.')';
		
		$rs = $db->query( 'SELECT COUNT(*) AS count FROM inctypes WHERE '.$where );
		if( $rs->fetchObject()->count != count($itp_ids) )
			throw new Exception(ERR_M::DATA_OWNER_DISABLED, 99);
		$rs->closeCursor();
		
// Удаляем записи из справочника
        $db->exec( "DELETE FROM inctypes WHERE itp_id IN ( ${itp_id_list} )" );
	}
	catch (PDOException $e) {
		$errcode = 1;
		$errmess = ERR_M::DATABASE_ERROR;
	}
    catch ( Exception $e ) {
		$errcode = $e->getCode();
		$errmess = $e->getMessage();
	}

	return array('errcode'=>$errcode,'errmess'=>$errmess);
  }
  
  
  private function is_itp_used_anyware( $db, $itp_id_list ) {
	$errdata = '';
	$sql =	"SELECT 'Приход по заказчикам' as a FROM `incpays` WHERE incp_itp_id IN ( ${itp_id_list} ) LIMIT 1";
	$rs = $db->query($sql);
	while( $row = $rs->fetchObject() ) $errdata .= ($errdata?', ':'')."'".$row->a."'";
	$rs->closeCursor();
	return $errdata;
  }


// ***************************************************
// Справочник видов исходящих платежей
//  
  
  public function listotp() {
  
    $data = new jqGridData();
    try
    {
// Только зарегистрированные пользователи могут видеть данные
		if( !isset($_SESSION['user_id']) || $_SESSION['user_id'] == 0 )
			throw new Exception(ERR_M::TIMEOUT_ERROR, 999);

		$rwd = $_SESSION['priv']->outtypes->rwd;
		if( !$rwd  ) throw new Exception(ERR_M::DATA_ACC_DISABLED, 99);

// Проверяем данные
        $jtp_id = isset($_REQUEST["jtp_id"]) ? $_REQUEST["jtp_id"] : '';
		if( $jtp_id == '' || !ctype_digit($jtp_id) )
			throw new Exception(ERR_M::PROTOCOL_ERROR, 1);

        $page    = ( isset($_REQUEST["page"]) && $_REQUEST["page"] > 0 ) ? $_REQUEST["page"] : 1;
        $limit    = ( isset($_REQUEST["rows"]) && $_REQUEST["rows"] > 0 ) ? $_REQUEST["rows"] : 1;
        $sidx    = ( isset($_REQUEST["sidx"]) && $_REQUEST["sidx"] != '' ) ? $_REQUEST["sidx"] : 'otp_ord';
        $sord    = ( isset($_REQUEST["sord"]) && strtolower($_REQUEST["sord"]) == 'desc' ) ? 'DESC' : 'ASC';
        $search    = ( isset($_REQUEST["_search"]) && strtolower($_REQUEST["_search"]) == 'true') ? true : false;
 
// Открываем базу данных
		$db = App::me()->dbOpen();	

		switch( $sidx ) {
			case 'otp_ord': $sidx = 'outl.otp_ord'; break;
			case 'otp_code': $sidx = 'outl.otp_code'; break;
			case 'otp_descr': $sidx = 'outl.otp_descr'; break;
			case 'btp_code': $sidx = 'bal.btp_code'; break;
		}
		
// Фильтр
        $whc = array();
		$whc[] = 'otp_jtp_id = '.$db->quote($jtp_id);
		
        if( $search )
        {
            if(isset($_REQUEST["otp_ord"]) && $_REQUEST["otp_ord"] != "") {
		    	$whc[] = "outl.otp_ord = " . $db->quote($_REQUEST["otp_ord"]);
            }
			
            if(isset($_REQUEST["otp_code"]) && $_REQUEST["otp_code"] != "") {
		    	$whc[] = "outl.otp_code LIKE " . $db->quote("%".$_REQUEST["otp_code"]."%");
            }
			
            if(isset($_REQUEST["otp_descr"]) && $_REQUEST["otp_descr"] != "") {
		    	$whc[] = "outl.otp_descr LIKE " . $db->quote("%".$_REQUEST["otp_descr"]."%");
            }
            if(isset($_REQUEST["btp_code"]) && $_REQUEST["btp_code"] != "") {
		    	$whc[] = "bal.btp_code LIKE " . $db->quote("%".$_REQUEST["btp_code"]."%");
            }
			
		}

// Фильтр привилегий
		if( $rwd & PERM_BITS::READ_OTH ) $whc[] = '1 = 1';
		elseif( $rwd & PERM_BITS::READ_GRP ) 
			$whc[] = 'outl.otp_user_id IN (SELECT user_id FROM users WHERE cat_id = '.$_SESSION['cat_id'].')';
		elseif( $rwd & PERM_BITS::READ_OWN ) $whc[] = 'outl.otp_user_id = '.$_SESSION['user_id'];
		else $whc[] = '1 = 0';
		
        $whereclaus = (count($whc) != 0 ) ? " WHERE " . implode(" AND ", $whc) : "";
		unset($whc);
            
// JOINS
		$joins = "LEFT JOIN baltypes AS bal ON bal.btp_id = outl.otp_btp_id ";
		
// Получаем количество записей с учетом фильтра           
        $sql =	"SELECT COUNT(*) AS count FROM outtypes AS outl ".$joins.$whereclaus;
        $rs =	$db->query( $sql );
		$row =	$rs->fetchObject();
		$count = $row->count;
		$rs->closeCursor();
            
// Пустая таблица
        if ($count == 0)
			throw new Exception(ERR_M::SUCCESS, 0);
            
// Рассчитаем сколько всего страниц займут данные в БД
    	if( $count > 0 && $limit > 0) {
    		$total_pages = ceil($count/$limit);
    	} else {
    		$total_pages = 0;
    	}                  
 
		if ($page > $total_pages) $page=$total_pages;                  
 
		$start = $limit*$page - $limit;                   
		if($start < 0) $start = 0;                  
 
// Формируем запрос
		$sql =	"SELECT otp_id, otp_ord, otp_code, otp_descr, otp_btp_id AS btp_id, bal.btp_code AS btp_code ".
				"FROM outtypes AS outl ".$joins.$whereclaus." ORDER BY ".$sidx." ".$sord.
		        " LIMIT ".$start.", ".$limit;

		$rs =	$db->query($sql);                  
 
// Формирование объекта для возврата
        
    	$data->page      = $page;
    	$data->total     = $total_pages;
    	$data->records   = $count;
    		
    // Строки данных для таблицы
		$i = 0;
		while($row = $rs->fetchObject()) {
		
			$data->rows[$i]['id'] = $row->otp_id;
			$data->rows[$i]['cell'] = array(
					$row->otp_id,
					$row->otp_ord,
					$row->otp_code,
					$row->otp_descr,
					$row->btp_id,
					$row->btp_code
		    );
		    $i++;
		}
    }
	catch (PDOException $e) {
		$data->userdata->errcode = 1;
		$data->userdata->errmess = ERR_M::DATABASE_ERROR;
	}
    catch ( Exception $e ) {
		$data->userdata->errcode = $e->getCode();
		$data->userdata->errmess = $e->getMessage();
	}
 
	return $data;
  }
  
  
  public function findotp() {
	$errcode = 0;
	$errmess = '';
    $data = array();
	
    $term   = isset($_REQUEST["term"]) ? trim(strip_tags($_REQUEST["term"])) : '';
    $jtp_id   = isset($_REQUEST["jtp_id"]) ? trim(strip_tags($_REQUEST["jtp_id"])) : '';


    try
    {
// Только зарегистрированные пользователи могут видеть данные
		if( !isset($_SESSION['user_id']) || $_SESSION['user_id'] == 0 )
			throw new Exception(ERR_M::TIMEOUT_ERROR, 999);

// Маска должна быть READ_ANY
		$rwd = $_SESSION['priv']->outtypes->rwd;
		if( ( $rwd & PERM_BITS::READ_ANY ) == 0 ) throw new Exception(ERR_M::DATA_OPER_DISABLED, 99);

// Проверяем данные
        if ($term == '' || !$jtp_id || !ctype_digit($jtp_id) )
			throw new Exception(ERR_M::PROTOCOL_ERROR, 1);
   
// Открываем базу данных
		$db = App::me()->dbOpen();	

// Фильтр привилегий
		if( $rwd & PERM_BITS::READ_OTH ) $whs = '1 = 1';
		elseif( $rwd & PERM_BITS::READ_GRP ) 
			$whs = 'otp_user_id IN (SELECT user_id FROM users WHERE cat_id = '.$_SESSION['cat_id'].')';
		elseif( $rwd & PERM_BITS::READ_OWN ) $whs = 'otp_user_id = '.$_SESSION['user_id'];
		else $whs = '1 = 0';
		
// Формируем запрос.
		$sql =  "SELECT otp_id, otp_code, otp_descr ".
				"FROM outtypes ".
				"WHERE otp_jtp_id = ".$db->quote($jtp_id)." AND ( otp_code LIKE ".$db->quote("%".$term."%").
				" OR otp_descr LIKE ".$db->quote("%".$term."%").' ) AND '.$whs." ORDER BY otp_ord";
					
		$rs = $db->query($sql);                  
        while( $row = $rs->fetchObject() ) {
            $item = new stdClass;
            $item->otp_id = $row->otp_id;
			
			$num = mb_strlen($term,'UTF-8');
			$pos = mb_stripos( $row->otp_code, $term, 0, 'UTF-8');
			if( !is_bool($pos) ) {
				$bterm = mb_substr($row->otp_code, $pos, $num, 'UTF-8');
				$name = preg_replace( '/'.$bterm.'/u', '<b>'.$bterm.'</b>', $row->otp_code );				
			} else $name = $row->otp_code;
			$pos = mb_stripos( $row->otp_descr, $term, 0, 'UTF-8');
			if( !is_bool($pos) ) {
				$bterm = mb_substr($row->otp_descr, $pos, $num, 'UTF-8');
				$full = preg_replace( '/'.$bterm.'/u', '<b>'.$bterm.'</b>', $row->otp_descr );				
			} else $full = $row->otp_descr;
			
            $item->otp_code = $name;
			$item->otp_descr = $full;
            $data[] = $item;
        }
        $rs->closeCursor();
 
    }
	catch (PDOException $e) {
		$errcode = 1;
		$errmess = ERR_M::DATABASE_ERROR;
	}
    catch ( Exception $e ) {
		$errcode = $e->getCode();
		$errmess = $e->getMessage();
	}
 
	return array('errcode'=>$errcode,'errmess'=>$errmess, 'data'=>$data);
  }
  
  
  public function findallotp() {
	$errcode = 0;
	$errmess = '';
    $data = array();
	
    $term   = isset($_REQUEST["term"]) ? trim(strip_tags($_REQUEST["term"])) : '';

    try
    {
// Только зарегистрированные пользователи могут видеть данные
		if( !isset($_SESSION['user_id']) || $_SESSION['user_id'] == 0 )
			throw new Exception(ERR_M::TIMEOUT_ERROR, 999);

// Маска должна быть READ_ANY
		$rwd = $_SESSION['priv']->outtypes->rwd;
		if( ( $rwd & PERM_BITS::READ_ANY ) == 0 ) throw new Exception(ERR_M::DATA_OPER_DISABLED, 99);

// Проверяем данные
        if ($term == '' )
			throw new Exception(ERR_M::PROTOCOL_ERROR, 1);
   
// Открываем базу данных
		$db = App::me()->dbOpen();	

// Фильтр привилегий
		if( $rwd & PERM_BITS::READ_OTH ) $whs = '1 = 1';
		elseif( $rwd & PERM_BITS::READ_GRP ) 
			$whs = 'otp_user_id IN (SELECT user_id FROM users WHERE cat_id = '.$_SESSION['cat_id'].')';
		elseif( $rwd & PERM_BITS::READ_OWN ) $whs = 'otp_user_id = '.$_SESSION['user_id'];
		else $whs = '1 = 0';
		
// Формируем запрос.
		$sql =  "SELECT otp_id, otp_code, otp_descr ".
				"FROM outtypes ".
				"WHERE ( otp_code LIKE ".$db->quote("%".$term."%").
				" OR otp_descr LIKE ".$db->quote("%".$term."%").' ) AND '.$whs." ORDER BY otp_jtp_id, otp_ord";
					
		$rs = $db->query($sql);                  
        while( $row = $rs->fetchObject() ) {
            $item = new stdClass;
            $item->otp_id = $row->otp_id;
			
			$num = mb_strlen($term,'UTF-8');
			$pos = mb_stripos( $row->otp_code, $term, 0, 'UTF-8');
			if( !is_bool($pos) ) {
				$bterm = mb_substr($row->otp_code, $pos, $num, 'UTF-8');
				$name = preg_replace( '/'.$bterm.'/u', '<b>'.$bterm.'</b>', $row->otp_code );				
			} else $name = $row->otp_code;
			$pos = mb_stripos( $row->otp_descr, $term, 0, 'UTF-8');
			if( !is_bool($pos) ) {
				$bterm = mb_substr($row->otp_descr, $pos, $num, 'UTF-8');
				$full = preg_replace( '/'.$bterm.'/u', '<b>'.$bterm.'</b>', $row->otp_descr );				
			} else $full = $row->otp_descr;
			
            $item->otp_code = $name;
			$item->otp_descr = $full;
            $data[] = $item;
        }
        $rs->closeCursor();
 
    }
	catch (PDOException $e) {
		$errcode = 1;
		$errmess = ERR_M::DATABASE_ERROR;
	}
    catch ( Exception $e ) {
		$errcode = $e->getCode();
		$errmess = $e->getMessage();
	}
 
	return array('errcode'=>$errcode,'errmess'=>$errmess, 'data'=>$data);
  }
  
  
  public function insotp() {

    $jtp_id		= isset($_POST['jtp_id']) ? trim(strip_tags($_POST['jtp_id'])) : '';
    $otp_ord	= isset($_POST['otp_ord']) ? trim(strip_tags($_POST['otp_ord'])) : '';
    $otp_code	= isset($_POST['otp_code']) ? trim(strip_tags($_POST['otp_code'])) : '';
    $otp_descr	= isset($_POST['otp_descr']) ? trim(strip_tags($_POST['otp_descr'])) : '';
    $btp_id		= isset($_POST['btp_id']) ? trim(strip_tags($_POST['btp_id'])) : '';
    $btp_code	= isset($_POST['btp_code']) ? trim(strip_tags($_POST['btp_code'])) : '';

	$errcode = 0;
	$errmess = '';
    try
    {
// Открываем базу данных
		$db = App::me()->dbOpen();	

// Только зарегистрированные пользователи могут вносить данные
		if( !isset($_SESSION['user_id']) || $_SESSION['user_id'] == 0 )
			throw new Exception(ERR_M::TIMEOUT_ERROR, 999);
		$otp_user_id = $_SESSION['user_id'];
		
		if( !$jtp_id || !ctype_digit($jtp_id) || !$btp_id || !ctype_digit($btp_id) )
			throw new Exception(ERR_M::PROTOCOL_ERROR, 1);
			
//Есть ли разрешение на вставку записей
		$rwd = $_SESSION['priv']->outtypes->rwd;
		if( ($rwd & PERM_BITS::WRITE_OWN) == 0 ) throw new Exception(ERR_M::DATA_OPER_DISABLED, 99);

// Все ли данные переданы
		$errdata = "";
		
// otp_ord			
		if( !$otp_ord || !ctype_digit($otp_ord) ) 
			$errdata .= ($errdata?'<br>':'').'- Не указан или указан неправильно порядковый номер.';
			
// otp_code			
		if( !$otp_code  ) $errdata .= ($errdata?'<br>':'').'- Не указан шифр расхода.';
		else {
//			$otp_code = mb_strtoupper( $otp_code, 'UTF-8' );
//			$otp_code = preg_replace( '/[^А-ЯІЇA-Z0-9]/u', '', $otp_code );
			$otp_code = mb_substr( $otp_code, 0, 16, 'UTF-8' );
		}

// otp_descr
		if( !$otp_descr ) $errdata .= ($errdata?'<br>':'').'- Не задана расшифровка вида расхода.';
		else $otp_descr = mb_substr( $otp_descr, 0, 128, 'UTF-8');

// Есть ли такой код баланса и соотв. ли он шифру
		$rs = $db->query( 'SELECT btp_code FROM baltypes WHERE btp_id = '.$db->quote($btp_id).
							' AND btp_jtp_id = '.$db->quote($jtp_id).' LIMIT 1' );		
		$row = $rs->fetchObject();
		if( !$row || $row->btp_code != $btp_code )
			$errdata .= ($errdata?'<br>':'').'- Неправильно задан код баланса.';
		$rs->closeCursor();

//				
// Если есть ошибки - можно уже закончить
//
		if( $errdata ) throw new Exception($errdata, 1);
		
// Может этот вид прихода уже есть
		$rs = $db->query( "SELECT '1' FROM outtypes WHERE otp_code = ".$db->quote($otp_code).
					" AND otp_jtp_id = ".$db->quote($jtp_id)." LIMIT 1" );
		if( $rs->fetchObject() )
			throw new Exception('В справочнике уже присутствует запись с шифром "'.$otp_code.'"', 1);
		$rs->closeCursor();

// Пишем данные
        $sql =  'INSERT outtypes SET '.
					'otp_jtp_id = '.$db->quote($jtp_id)		.", ".
					'otp_ord = '  .$db->quote($otp_ord)	.", ".
					'otp_code = '  .$db->quote($otp_code)	.", ".
					'otp_descr = ' .$db->quote($otp_descr)	.", ".
					'otp_btp_id = '.$db->quote($btp_id)		.", ".
					'otp_user_id = ' .$db->quote($otp_user_id);
												
		$db->exec($sql);
    }
	catch (PDOException $e) {
		$errcode = 1;
		$errmess = ERR_M::DATABASE_ERROR;
	}
    catch ( Exception $e ) {
		$errcode = $e->getCode();
		$errmess = $e->getMessage();
	}

	return array('errcode'=>$errcode,'errmess'=>$errmess);
  }

  public function updotp() {

    $jtp_id		= isset($_POST['jtp_id']) ? trim(strip_tags($_POST['jtp_id'])) : '';
    $otp_id		= isset($_POST['id']) ? trim(strip_tags($_POST['id'])) : '';
    $otp_ord	= isset($_POST['otp_ord']) ? trim(strip_tags($_POST['otp_ord'])) : '';
    $otp_code	= isset($_POST['otp_code']) ? trim(strip_tags($_POST['otp_code'])) : '';
    $btp_id		= isset($_POST['btp_id']) ? trim(strip_tags($_POST['btp_id'])) : '';
    $btp_code	= isset($_POST['btp_code']) ? trim(strip_tags($_POST['btp_code'])) : '';
    $otp_descr	= isset($_POST['otp_descr']) ? trim(strip_tags($_POST['otp_descr'])) : '';
	
	$errcode = 0;
	$errmess = '';
    try
    {
// Открываем базу данных
		$db = App::me()->dbOpen();	

// Только зарегистрированные пользователи могут вносить данные
		if( !isset($_SESSION['user_id']) || $_SESSION['user_id'] == 0 )
			throw new Exception(ERR_M::TIMEOUT_ERROR, 999);
		$otp_user_id = $_SESSION['user_id'];
		
		if( !$jtp_id || !ctype_digit($jtp_id) || !$btp_id || !ctype_digit($btp_id) || !$otp_id || !ctype_digit($otp_id) )
			throw new Exception(ERR_M::PROTOCOL_ERROR, 1);
			
//Есть ли разрешение на вставку записей
		$rwd = $_SESSION['priv']->outtypes->rwd;
		if( ($rwd & PERM_BITS::WRITE_ANY) == 0 ) throw new Exception(ERR_M::DATA_OPER_DISABLED, 99);

// Все ли данные переданы
		$errdata = "";
		
// otp_ord			
		if( !$otp_ord || !ctype_digit($otp_ord) ) 
			$errdata .= ($errdata?'<br>':'').'- Не указан или указан неправильно порядковый номер.';
			
// otp_code			
		if( !$otp_code  ) $errdata .= ($errdata?'<br>':'').'- Не указан шифр расхода.';
		else {
//			$otp_code = mb_strtoupper( $otp_code, 'UTF-8' );
//			$otp_code = preg_replace( '/[^А-ЯІЇA-Z0-9]/u', '', $otp_code );
			$otp_code = mb_substr( $otp_code, 0, 16, 'UTF-8' );
		}

// otp_descr
		if( !$otp_descr ) $errdata .= ($errdata?'<br>':'').'- Не задана расшифровка вида расхода.';
		else $otp_descr = mb_substr( $otp_descr, 0, 128, 'UTF-8');

// Есть ли такой код баланса и соотв. ли он шифру
		$rs = $db->query( 'SELECT btp_code FROM baltypes WHERE btp_id = '.$db->quote($btp_id).
							' AND btp_jtp_id = '.$db->quote($jtp_id).' LIMIT 1' );		
		$row = $rs->fetchObject();
		if( !$row || $row->btp_code != $btp_code )
			$errdata .= ($errdata?'<br>':'').'- Неправильно задан код баланса.';
		$rs->closeCursor();

//				
// Если есть ошибки - можно уже закончить
//
		if( $errdata ) throw new Exception($errdata, 1);
		
// Проверяем задействована ли эта запись в других таблицах
		if( ( $errdata = $this->is_otp_used_anyware( $db, $otp_id )) != '' )
			throw new Exception( "Этот вид расхода уже задействован в следующих документах:<br>".$errdata, 1);

// Есть ли разрешение на апдейт этой записи
		if( $rwd & PERM_BITS::WRITE_OTH ) $where = '1 = 1';
		elseif( $rwd & PERM_BITS::WRITE_GRP ) 
			$where = 'otp_user_id IN (SELECT user_id FROM users WHERE cat_id = '.$_SESSION['cat_id'].')';
		elseif( $rwd & PERM_BITS::WRITE_OWN ) $where = 'otp_user_id = '.$_SESSION['user_id'];
		else $where = '1 = 0';
		$where .= ' AND otp_id = '.$db->quote($otp_id);
		
		$rs = $db->query( 'SELECT otp_code FROM outtypes WHERE '.$where.' LIMIT 1' );
		if( !$rs->fetchObject() )
			throw new Exception(ERR_M::DATA_OWNER_DISABLED, 99);
		$rs->closeCursor();
		
// Может этот вид прихода уже есть
		$rs = $db->query( "SELECT '1' FROM outtypes WHERE otp_code = ".$db->quote($otp_code).
					" AND otp_jtp_id = ".$db->quote($jtp_id)." AND otp_id != ".$db->quote($otp_id)." LIMIT 1" );
		if( $rs->fetchObject() )
			throw new Exception('В справочнике уже присутствует запись с шифром "'.$otp_code.'"', 1);
		$rs->closeCursor();

// Пишем данные
        $sql =  'UPDATE outtypes SET '.
					'otp_ord = ' .$db->quote($otp_ord).", ".
					'otp_code = ' .$db->quote($otp_code).", ".
					'otp_btp_id = ' .$db->quote($btp_id).", ".
					'otp_descr = '.$db->quote($otp_descr)." ".
				'WHERE otp_id = '.$db->quote($otp_id);
					
		$db->exec($sql);
    }
	catch (PDOException $e) {
		$errcode = 1;
		$errmess = ERR_M::DATABASE_ERROR;
	}
    catch ( Exception $e ) {
		$errcode = $e->getCode();
		$errmess = $e->getMessage();
	}

	return array('errcode'=>$errcode,'errmess'=>$errmess);
  }

  
  public function delotp() {
    $errmess = "";
	$errcode = 0;
		
    $otp_ids = (isset($_REQUEST['id']) && $_REQUEST['id'] != '') ?
        explode(",", $_REQUEST["id"], 100) : array();

    try
    {
// Только зарегистрированные пользователи могут удалять данные
		if( !isset($_SESSION['user_id']) || $_SESSION['user_id'] == 0 )
			throw new Exception(ERR_M::TIMEOUT_ERROR, 999);

// Есть ли разрешение на удаление хоть какой либо записи
		$rwd = $_SESSION['priv']->outtypes->rwd;
		if( ($rwd & PERM_BITS::DELETE_ANY) == 0 ) throw new Exception(ERR_M::DATA_OPER_DISABLED, 99);
		
        if (!Tools::is_numeric_array( $otp_ids ))
			throw new Exception(ERR_M::PROTOCOL_ERROR, 1);
        
		$otp_id_list = implode(',', $otp_ids);

// Открываем базу данных
		$db = App::me()->dbOpen();	

// Проверяем задействованы ли эти записи в других таблицах
		if( ( $errdata = $this->is_otp_used_anyware( $db, $otp_id_list )) != '' )
			throw new Exception( "В списке удаляемых записей присутствует запись(си) уже задействованная в следующих документах:<br>".$errdata, 1);

// Есть ли разрешение на удаление этих записей
		if( $rwd & PERM_BITS::DELETE_OTH ) $where = '1 = 1';
		elseif( $rwd & PERM_BITS::DELETE_GRP ) 
			$where = 'otp_user_id IN (SELECT user_id FROM users WHERE cat_id = '.$_SESSION['cat_id'].')';
		elseif( $rwd & PERM_BITS::DELETE_OWN ) $where = 'btp_user_id = '.$_SESSION['user_id'];
		else $where = '1 = 0';
		$where .= ' AND otp_id IN ('.$otp_id_list.')';
		
		$rs = $db->query( 'SELECT COUNT(*) AS count FROM outtypes WHERE '.$where );
		if( $rs->fetchObject()->count != count($otp_ids) )
			throw new Exception(ERR_M::DATA_OWNER_DISABLED, 99);
		$rs->closeCursor();
		
// Удаляем записи из справочника
        $db->exec( "DELETE FROM outtypes WHERE otp_id IN ( ${otp_id_list} )" );
	}
	catch (PDOException $e) {
		$errcode = 1;
		$errmess = ERR_M::DATABASE_ERROR;
	}
    catch ( Exception $e ) {
		$errcode = $e->getCode();
		$errmess = $e->getMessage();
	}

	return array('errcode'=>$errcode,'errmess'=>$errmess);
  }
  
  
  private function is_otp_used_anyware( $db, $otp_id_list ) {
	$errdata = '';
	$sql =	"SELECT 'Расходы по контрагентам' as a FROM `outpays` WHERE outp_otp_id IN ( ${otp_id_list} ) LIMIT 1";
	$rs = $db->query($sql);
	while( $row = $rs->fetchObject() ) $errdata .= ($errdata?', ':'')."'".$row->a."'";
	$rs->closeCursor();
	return $errdata;
  }
  
} // End Of payments class 
?>